package cz.muni.fi.pv168.ContactManager;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by IntelliJ IDEA.
 * User: Vlado
 * Date: 4.3.2012
 * Time: 17:33
 * To change this template use File | Settings | File Templates.
 */
public class PersonManagerImpl implements PersonManager {

    private DataSource dataSource;

    final static Logger log = LoggerFactory.getLogger(PersonManagerImpl.class);

    public void setDataSource(DataSource ds)
    {
        this.dataSource = ds;
    }
    
    @Override
    public Person createPerson(Person person) throws ServiceFailureException {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        if(person == null) throw new NullPointerException("person");
        if(person.getId() != null) throw new IllegalEntityException("Id is not null!");
        Connection conn = null;
        PreparedStatement st = null;
        try {
                conn = dataSource.getConnection();
                st = conn.prepareStatement(
                        "INSERT INTO PERSON (FIRSTNAME,LASTNAME) VALUES (?,?)",
                        Statement.RETURN_GENERATED_KEYS);
                st.setString(1, person.getFirstName());
                st.setString(2, person.getLastName());

                int count = st.executeUpdate();
                if (count != 1)
                {
                    String msg = "Error when inserting person into db";
                    log.error(msg);
                    throw new ServiceFailureException(msg);
                }
                ResultSet key = st.getGeneratedKeys();
                Long id = null;
                key.next();
                id = key.getLong(1);
                if(id == null) return null;
                person.setId(id);
            } catch (SQLException ex) {
                String msg = "Error when inserting person into db";
                log.error(msg,ex);
                throw new ServiceFailureException(msg, ex);
            } finally {
                 try {if(st != null) {st.close();}}
                 catch (SQLException ex) {log.error("Error when closing statement", ex);}
                 try {if(conn != null) {conn.close();}}
                 catch (SQLException ex) {log.error("Error when closing connection", ex);}
            }
        return person;
    }

    @Override
    public void deletePerson(Person person) {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        if(person == null) throw new NullPointerException("person");
        if(person.getId() == null) throw new IllegalEntityException("Id is null!");
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = dataSource.getConnection();
            st = conn.prepareStatement(
                    "DELETE FROM PERSON WHERE ID = ?");
            st.setLong(1, person.getId());
            int count = st.executeUpdate();
            if (count > 1)
            {
                String msg = "More than one person with same ID";
                log.error(msg);
                throw new ServiceFailureException(msg);
            }
            if (count == 0){
                String msg = "No person with the ID";
                log.error(msg);
                throw new IllegalEntityException(msg);
            }
        } catch (SQLException ex) {
            String msg = "Error when deleting person from db";
            log.error(msg,ex);
            throw new ServiceFailureException(msg, ex);
        } finally {
            try {if(st != null) {st.close();}}
            catch (SQLException ex) {log.error("Error when closing statement", ex);}
            try {if(conn != null) {conn.close();}}
            catch (SQLException ex) {log.error("Error when closing connection", ex);}
        }
    }

    @Override
    public void updatePerson(Person person) {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        if(person == null) throw new NullPointerException("person");
        if(person.getId() == null) throw new IllegalEntityException("Id is null!");
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = dataSource.getConnection();
            st = conn.prepareStatement(
                    "UPDATE PERSON SET FIRSTNAME = ?, LASTNAME = ? WHERE ID = ?");
            st.setString(1, person.getFirstName());
            st.setString(2, person.getLastName());
            st.setLong(3, person.getId());

            int count = st.executeUpdate();
            if (count != 1)
            {
                String msg = "Error when updating person in db";
                log.error(msg);
                throw new ServiceFailureException(msg);
            }
        } catch (SQLException ex) {
            String msg = "Error when updating person in db";
            log.error(msg,ex);
            throw new ServiceFailureException(msg, ex);
        } finally {
            try {if(st != null) {st.close();}}
            catch (SQLException ex) {log.error("Error when closing statement", ex);}
            try {if(conn != null) {conn.close();}}
            catch (SQLException ex) {log.error("Error when closing connection", ex);}
        }
    }

    @Override
    public Person findPersonById(Long id) throws ServiceFailureException {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        if(id == null) throw new NullPointerException("id");
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = dataSource.getConnection();
            st = conn.prepareStatement(
                    "SELECT ID, FIRSTNAME, LASTNAME FROM PERSON WHERE ID = ?");
            st.setLong(1, id);
            ResultSet rs = st.executeQuery();

            if (rs.next())
            {
                Person result = rowToPerson(rs);
                if (rs.next())
                {
                    String msg = "More persons with same ID";
                    log.error(msg);
                    throw new ServiceFailureException(msg);
                }
                return result;
            }
            else
            {
                return null;
            }

        } catch (SQLException ex) {
            String msg = "Error when getting person from db";
            log.error(msg,ex);
            throw new ServiceFailureException(msg, ex);
        } finally {
            try {if(st != null) {st.close();}}
            catch (SQLException ex) {log.error("Error when closing statement", ex);}
            try {if(conn != null) {conn.close();}}
            catch (SQLException ex) {log.error("Error when closing connection", ex);}
        }
    }

    @Override
    public List<Person> findPersonsByFirstName(String firstName) {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        if(firstName == null) throw new NullPointerException("firstname");
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = dataSource.getConnection();
            st = conn.prepareStatement(
                    "SELECT ID, FIRSTNAME, LASTNAME FROM PERSON WHERE FIRSTNAME = ?");
            st.setString(1,firstName);
            ResultSet rs = st.executeQuery();
            List<Person> result = new ArrayList<Person>();
            while (rs.next()) {
                result.add(rowToPerson(rs));
            }
            return result;
        } catch (SQLException ex) {
            String msg = "Error when getting persons from db";
            log.error(msg,ex);
            throw new ServiceFailureException(msg, ex);
        } finally {
            try {if(st != null) {st.close();}}
            catch (SQLException ex) {log.error("Error when closing statement", ex);}
            try {if(conn != null) {conn.close();}}
            catch (SQLException ex) {log.error("Error when closing connection", ex);}
        }
    }

    @Override
    public List<Person> findPersonsByLastName(String lastName) {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        if(lastName == null) throw new NullPointerException("firstname");
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = dataSource.getConnection();
            st = conn.prepareStatement(
                    "SELECT ID, FIRSTNAME, LASTNAME FROM PERSON WHERE LASTNAME = ?");
            st.setString(1,lastName);
            ResultSet rs = st.executeQuery();
            List<Person> result = new ArrayList<Person>();
            while (rs.next()) {
                result.add(rowToPerson(rs));
            }
            return result;
        } catch (SQLException ex) {
            String msg = "Error when getting persons from db";
            log.error(msg,ex);
            throw new ServiceFailureException(msg, ex);
        } finally {
            try {if(st != null) {st.close();}}
            catch (SQLException ex) {log.error("Error when closing statement", ex);}
            try {if(conn != null) {conn.close();}}
            catch (SQLException ex) {log.error("Error when closing connection", ex);}
        }
    }

    @Override
    public List<Person> findAllPersons() {
        if(dataSource == null) throw new IllegalStateException("Data source is not set!");
        Connection conn = null;
        PreparedStatement st = null;
        try {
            conn = dataSource.getConnection();
            st = conn.prepareStatement(
                    "SELECT ID, FIRSTNAME, LASTNAME FROM PERSON");
            ResultSet rs = st.executeQuery();
            List<Person> result = new ArrayList<Person>();
            while (rs.next()) {
                result.add(rowToPerson(rs));
            }
            return result;
        } catch (SQLException ex) {
            String msg = "Error when getting persons from db";
            log.error(msg,ex);
            throw new ServiceFailureException(msg, ex);
        } finally {
            try {if(st != null) {st.close();}}
            catch (SQLException ex) {log.error("Error when closing statement", ex);}
            try {if(conn != null) {conn.close();}}
            catch (SQLException ex) {log.error("Error when closing connection", ex);}
        }
    }

    private static Person rowToPerson(ResultSet rs) throws SQLException {
        Person result = new Person();
        result.setId(rs.getLong("ID"));
        result.setFirstName(rs.getString("FIRSTNAME"));
        result.setLastName(rs.getString("LASTNAME"));
        return result;
    }


}
